package com.elinkway.infinitemovies.download;


import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Base64;

import com.elinkway.infinitemovies.application.MoviesApplication;
import com.elinkway.infinitemovies.async.NewRequestResultListener;
import com.elinkway.infinitemovies.async.RequestOutSiteStreamInfoTask;
import com.elinkway.infinitemovies.async.RequestResultListener;
import com.elinkway.infinitemovies.async.UpLoadLocalStreamTask;
import com.elinkway.infinitemovies.bean.OutSiteStreamEntity;
import com.elinkway.infinitemovies.bean.OutSiteStreamInfo;
import com.elinkway.infinitemovies.bean.PlayStatusReporter;
import com.elinkway.infinitemovies.bean.UpLoadLocalStreamBean;
import com.elinkway.infinitemovies.config.SettingManage;
import com.elinkway.infinitemovies.http.requesttask.ReportPlayStatusTask;
import com.elinkway.infinitemovies.listener.PlayOrDownJsSniffListener;
import com.elinkway.infinitemovies.selfdata.MyDataRecordCreater;
import com.elinkway.infinitemovies.selfdata.bean.MyDataRecord;
import com.elinkway.infinitemovies.selfdata.dao.RealmDaoManager;
import com.elinkway.infinitemovies.ui.activity.play.PlayerUtils;
import com.elinkway.infinitemovies.utils.LogCat;
import com.elinkway.infinitemovies.utils.LogUtils;
import com.elinkway.infinitemovies.utils.MoviesConstant;
import com.elinkway.infinitemovies.utils.NetWorkTypeUtils;
import com.elinkway.infinitemovies.utils.RequestSniffDataTaskPoolManager;
import com.elinkway.infinitemovies.utils.SharePreferenceManager;
import com.elinkway.infinitemovies.utils.StringUtil;
import com.elinkway.infinitemovies.utils.Utils;
import com.leeco.pp.entity.CvcTask;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;


public class DownloadJob {
    public static final String TAG = "DownloadJob";
    private DownloadEntity mEntity;
    private CvcTask mCvcTask;
    private int mProgress;
    private long mTotalSize;
    private long mDownloadedSize;
    private long mM3u8DownloadedSize;
    private String mRate;
    private long mOldTime = 0;
    private long mOldBytes = 0;
    private String mDestination;
    private int mIndex;

    private DownloadJobListener mListener;
    private DownloadManager mDownloadManager;
    private DownloadTask mDownloadTask;
    private int mRetryNum;
    private int mTotalRetryNum;
    private boolean autoSnifferRetry = true;//站外源，走截流时，自动重试走截流逻辑

    // job 的各种状态
    public static final int INIT = 5;
    public static final int DOWNLOADING = 2;
    public static final int PAUSE = 3; // 指用户人为暂停
    public static final int WAITING = 4;
    public static final int COMPLETE = 1;
    public static final int DELETE = 6;
    public static final int PAUSEONSPEED = 7;
    public static final int NO_USER_PAUSE = 0; // 指网络等其他原因非用户任务暂停
    private int mStatus = INIT;

    public final static int NET_TIMEOUT = 1;
    public final static int FILE_NOT_FOUND = 2;
    public final static int SD_SPACE_FULL = 3;
    public final static int NO_SD = 4;
    public final static int NET_SHUT_DOWN = 5;
    public final static int DOWNLOAD_FAILUER = 6;//下载失败
    public final static int OTHER_EXCEP = 9;
    private int mExceptionType;
    public DownloadHandler downloadHandler;
    private boolean isUserPauseWhen3G = true;

    private boolean isCheck = false;//删除选择时，判断是否被选中
    private ArrayList mStateList;

    //新外网截流 start
    private Long startTime;
    private Context mContext;
    private String mSnifferUrl;
    private RequestOutSiteStreamInfoTask mOutSiteStreamInfoTask;
    private String outSiteFormat = "";
    private UpLoadLocalStreamTask mUpLoadLocalStreamTask;
    private OutSiteStreamInfo mOutSiteStreamInfo;
    private List<OutSiteStreamEntity> mStreamEntityList;
    private int mStreamEntityListSize;
    private OutSiteStreamEntity mOutSiteStreamEntity;
    private boolean isNeedRetry; //再次请求流数据
    private int retryTime; //重试次数1次
    private List<Double> fSizeList; //所有下载文件的大小数组
    private double totalSize; //server返回的总大小
    private double totalSizeFromServer; //当前下载片段的文件大小
    private List<String> mAllowFormats;
    private String mRequestFormat;
    private String mOsType;
    private String mEid;
    private String mIsDownload;
    private List<String> mStream_list;
    private List<String> mApi_list;
    private List<String> mApi_contentlist;
    private DownloadRequestManager downloadRequestManager;
    private String mUser_agent;
    private boolean isHasRule;
    private Map<String, String> mJsRuleMap;
    private boolean isSpecialSniff;
    private String mTe;
    private String mTs;
    private int mCurStreamIndex;
    private boolean isNeedJsCut; // 是否需要本地截流
    private boolean isSniffDone; // js截流是否完成
    private int mApiContentListIndex; // apiContentList index
    private int mStreamListIndex; // mStreamList index
    private List<String> mResultUrlList; // 轮询截流最终下载url集合

    private int mCurrentDownloadPiece;

    private boolean mIsPause;
    private boolean mIsFail;
    private static String TE = "te";
    private static String TS = "ts";

    private boolean mIsAllow3GDownload;

    private boolean mIsPauseByNetWork;

    private boolean mIsDownloading;

    private int mLastConnectivityState;

    private PlayStatusReporter mPlayStatusReport = new PlayStatusReporter();

    private boolean mIsJSFailed;

    private boolean mIsJSSuccessed;


    public int getCurrentDownloadPiece() {
        return mCurrentDownloadPiece;
    }

    public void setCurrentDownloadPiece(int mCurrentDownloadPiece) {
        this.mCurrentDownloadPiece = mCurrentDownloadPiece;
    }

    public boolean isPause() {
        return mIsPause;
    }

    public void setIsPause(boolean status) {
        this.mIsPause = status;
    }

    public void setFail(boolean downFail) {
        this.mIsFail = downFail;
    }

    public boolean isFail() {
        return mIsFail;
    }

    public DownloadJob(DownloadEntity data, String destination) {
        mEntity = data;
        mDestination = destination;
        mDownloadedSize = DownloadHelper.getDownloadedFileSize(data, destination);
        mTotalSize = data.getFileSize();
        mProgress = initProgress();
        mStatus = data.getStatus();
        mTotalSize = data.getFileSize();
        mDownloadManager = MoviesApplication.getInstance().getDownloadManager();
        mContext = MoviesApplication.getInstance().getApplicationContext();
        mLastConnectivityState = NetWorkTypeUtils.getNetType();
        mIsAllow3GDownload = mContext.getSharedPreferences(SettingManage.SETTING_RELATIVE_SHAREPREFERENCE ,Context.MODE_PRIVATE)
            .getBoolean(SettingManage.IS_DOWNLOAD_CAN_3G, false);
        resetOutSiteData();// 重置外站源数据
    }

    /**
     * 下载地址无效时取消下载任务(未使用)
     *
     * @return
     */
    public boolean setDownloadTaskCancle() {
        if (null != mDownloadTask)
            return mDownloadTask.cancel(true);
        mDownloadTask = new DownloadTask(this);
        return false;
    }

    public int getmCurStreamIndex() {
        return mCurStreamIndex;
    }

    public String getmUser_agent() {
        return mUser_agent;
    }

    public int initProgress() {
        return mTotalSize == 0 ? 0 : (int) ((mDownloadedSize * 100) / mTotalSize);
    }

    public int getmTotalRetryNum() {
        return mTotalRetryNum;
    }

    public boolean getAutoSnifferRetry() {
        return autoSnifferRetry;
    }

    public boolean getCanChangeSniffer() {
        boolean nodownLoad = DownloadHelper.getDownloadedFileSize(mEntity, mEntity.getPath()) == 0;

        return (mTotalRetryNum == 7) && nodownLoad;
    }

    public DownloadEntity getEntity() {
        return mEntity;
    }

    public void setProgress(int mProgress) {
        this.mProgress = mProgress;
    }

    public int getProgress() {
        return mProgress;
    }

    public int getIndex() {
        return mIndex;
    }

    public void setIndex(int mIndex) {
        this.mIndex = mIndex;
    }

    public void setListener(DownloadJobListener mListener) {
        this.mListener = mListener;
    }

    public void setRetryNum(int retryNum) {
        this.mRetryNum = retryNum;
    }

    public void setExceptionType(int mExceptionType) {
        this.mExceptionType = mExceptionType;
    }

    public int getExceptionType() {
        return mExceptionType;
    }

    public boolean isUserPauseWhen3G() {
        return isUserPauseWhen3G;
    }

    public void setUserPauseWhen3G(boolean isUserPauseWhen3G) {
        this.isUserPauseWhen3G = isUserPauseWhen3G;
    }

    public DownloadManager getDownloadManager() {
        return mDownloadManager;
    }

    public void setDownloadManager(DownloadManager mDownloadManager) {
        this.mDownloadManager = mDownloadManager;
    }

    public void setStatus(int status) {
        this.mStatus = status;
    }

    public int getStatus() {
        return mStatus;
    }

    public String getDestination() {
        return mDestination;
    }

    public List<String> getStreamList() {
        return mStream_list;
    }
    public boolean isDownloadcan3g() {
        return mDownloadManager.IsDownloadcan3g();
    }

    public long getTotalSize() {
        return mTotalSize;
    }

    public double getTotleSizeFromServer() {
        return totalSizeFromServer;
    }

    public void setTotleSizeFromServer(int index){
        if (fSizeList != null && fSizeList.size() > index) {
            totalSizeFromServer = fSizeList.get(index);
        }
    }

    public void setTotalSize(long mTotalSize) {
        this.mTotalSize = mTotalSize;
    }

    public void setDownloadedSize(long mDownloadedSize) {
        this.mDownloadedSize = mDownloadedSize;
        int oldProgress = mProgress;
        if (mTotalSize == 0) {
            mProgress = 0;
        } else {
            mProgress = (int) ((mDownloadedSize * 100) / mTotalSize);
        }

        if (mProgress != oldProgress) {
            mDownloadManager.notifyObservers();
            notifyDownloadOnUpdate();
        }
    }

    public long getDownloadedSize() {
        return mDownloadedSize;
    }

    public void setRate() {
        long curTime = System.currentTimeMillis();

        if (((curTime - mOldTime) / 1000) >= 2) {

            mOldTime = curTime;
            this.mRate = getRate(mDownloadedSize - mOldBytes);
            mOldBytes = mDownloadedSize;
            mDownloadManager.notifyObservers();
            notifyDownloadOnUpdate();
        }
    }

    public void setM3u8Rate() {
        long curTime = System.currentTimeMillis();

        if (((curTime - mOldTime) / 1000) >= 2) {

            mOldTime = curTime;
            this.mRate = getRate(mM3u8DownloadedSize - mOldBytes);
            mOldBytes = mM3u8DownloadedSize;
            mDownloadManager.notifyObservers();
            notifyDownloadOnUpdate();
        }
    }

    public void setRate(String strRate) {
        mRate = strRate;
    }

    public String getRate() {
        if (TextUtils.isEmpty(mRate) || mStatus == WAITING) {
            mRate = "0.0KB/s";
        }
        return mRate;
    }

    public String getRate(long l) {
        float rate = (int) (l / 1024 / 2);
        LogUtils.e("", "Rate is " + rate);
        if (rate < 0.0)
            rate = 0.0f;
        return rate + "KB/s";
    }

	/*
     * public void setEntity(DownloadEntity mEntity) { this.mEntity = mEntity; }
	 * 
	 * public void setDestination(String mDestination) { this.mDestination =
	 * mDestination; }
	 */

    /**
     * 下载任务开始
     */
    @SuppressLint("NewApi")
    public void start() {
        int num = mDownloadManager.getMaxDownloadNum();
        mExceptionType = 0;
        startTime = System.currentTimeMillis();
        synchronized (DownloadJob.class) {
            LogUtils.d(CDNDownloadHandler.TAG, "current downloading num is :" + mDownloadManager.DOWNLOADING_NUM);
            if (mDownloadManager.DOWNLOADING_NUM < num) {
                LogUtils.d(CDNDownloadHandler.TAG, "add download job is :" + mEntity.getDisplayName());
                mDownloadManager.DOWNLOADING_NUM = 1;
                mStatus = DOWNLOADING;
                mDownloadTask = new DownloadTask(this);
                if (PlayerUtils.isOutSite(mEntity.getSite()) || ("letv".equals(mEntity.getSite()) && !"1".equals(mEntity.getIsletvjiexi()))) {
                    LogUtils.e(TAG, "!!!!!!!!on start() method called and down name is " + mEntity.getDisplayName());
                    LogUtils.e(TAG, "!!!!!!!!on start() method called and cur DownloadEntity order is " + mEntity.getIndex());
                    if (null == mOutSiteStreamInfo || null == mStreamEntityList || mStreamEntityList.isEmpty()) {
                        LogUtils.e(TAG, "##################mOutSiteStreamInfo is null############### and requestOutSiteStreamInfo called");
                        requestOutSiteStreamInfo(null);
                    } else {
                        LogUtils.e(TAG, "##################mOutSiteStreamInfo not null################## and excuteCoreDownLogic called");
                        excuteCoreDownLogic(mCurStreamIndex);
                    }
                } else {
                    excuteCommonDownTask();
                }

            } else {
                mStatus = WAITING;
                /**
                 * 做一次页面刷新，以便在下载截流很慢的情况下点击下一个任务下载加载菊花去不掉情况
                 */
                mDownloadManager.notifyObservers();
            }
            if (downloadHandler != null) {
                downloadHandler.onStart();
            }
            mDownloadManager.setStatus(mEntity, mStatus);
        }
    }

    /**
     * 下载完成
     */
    public void onCompleted() {
        LogUtils.i(CDNDownloadHandler.TAG, "下载成功：onCompleted and current complete job is:" + mEntity.getDisplayName());
        if (mIsJSFailed) {
            mPlayStatusReport.setEndRequestTime(System.currentTimeMillis());
            reportDownloadState(MoviesConstant.JS_ERROR_SERVER_DOWNLOAD_SUCCESS);
            mIsJSFailed = false;
        }
        mStatus = DownloadJob.COMPLETE;

        MyDataRecord record = MyDataRecordCreater.build(MyDataRecord.class);
        record.setDold("1");
        RealmDaoManager.addMyDataRecord(record);

        notifyDownloadOnPause();
        notifyDownloadEnded();
        mDownloadManager.DOWNLOADING_NUM = 0;
        mDownloadManager.startNextTask();
    }

    // DownLoadJobAdapter 中调用

    /**
     * 用户手动停止下载
     * @return
     */
    public boolean pauseByUser() {
        LogUtils.e(CDNDownloadHandler.TAG,"!!!!!!!!!!!!!!user pause and cancle mDownloadTask,,and current job is：" + mEntity.getDisplayName() + "!!!!!!!!!!!!!!");
        mStatus = PAUSE;
        mTotalRetryNum = 0;
        mDownloadManager.DOWNLOADING_NUM = 0;
        mDownloadManager.startNextTask();
        mDownloadManager.setStatus(mEntity, PAUSE);
        if (downloadHandler != null) {
            downloadHandler.onPause(this);
        }
        return mDownloadTask.cancel(true);
    }

    /**
     * 停止所有下载任务
     */
    public void pauseAllByUser() {
        LogUtils.e(CDNDownloadHandler.TAG,"!!!!!!!!!!!!!!pauseAllByUser!!!!!!!!!!!!!!");
        mStatus = PAUSE;
        mTotalRetryNum = 0;
        mDownloadManager.DOWNLOADING_NUM = 0;
        mDownloadManager.setStatus(mEntity, PAUSE);
        if (downloadHandler != null) {
            downloadHandler.onPause(this);
        }
        if(mDownloadTask != null) {
            mDownloadTask.cancel(true);
        }
    }

    // DownLoadJobAdapter 中调用
    public boolean pauseByNetWork() {
        mStatus = PAUSE;
        mTotalRetryNum = 0;
        mDownloadManager.DOWNLOADING_NUM = 0;
        mDownloadManager.setStatus(mEntity, PAUSE);
        if (downloadHandler != null) {
            downloadHandler.onPause(this);
        }
//        LogUtils.e(Utils.DOWN_TAG,"!!!!!!!!!!!!!!user pause and cancle mDownloadTask!!!!!!!!!!!!!!");
        return mDownloadTask.cancel(true);
    }

    /**
     * 下载失败后，置为暂停状态。和点击暂停有区别：DOWNLOADING_NUM不自减
     */
    public boolean pauseByDownLoadFailure() {
        LogUtils.e(CDNDownloadHandler.TAG,"!!!!!!!!!!!!!!pauseByDownLoadFailure and current job is:" + mEntity.getDisplayName() + "!!!!!!!!!!!!!!");
        mStatus = PAUSE;
        mTotalRetryNum = 0;
        mDownloadManager.startNextTask();
        mDownloadManager.setStatus(mEntity, PAUSE);
        if (downloadHandler != null) {
            downloadHandler.onPause(this);
        }
        return mDownloadTask.cancel(true);
    }

    public boolean allPause() {
        mStatus = PAUSE;
        mDownloadManager.DOWNLOADING_NUM = 0;
//		mDownloadManager.startNextTask();
        mDownloadManager.setStatus(mEntity, PAUSE);
        if (downloadHandler != null) {
            downloadHandler.onPause(this);
        }
        boolean result = false;
        if (null != mDownloadTask) {
            result = mDownloadTask.cancel(true);
        }
        return result;
    }

    /**
     * Waiting-->Pause
     */
    public void cancel() {
        LogUtils.e(CDNDownloadHandler.TAG,"!!!!!!!!!!!!!!cancel and state is pause, current job is:" + mEntity.getDisplayName());
        mStatus = PAUSE;
        mDownloadManager.setStatus(mEntity, PAUSE);
    }

    /**
     * 下载失败回调(类似于播放 onError()方法)
     */
    public void onFailure() {
        if (PlayerUtils.isOutSite(mEntity.getSite()) || ("letv".equals(mEntity.getSite()))) {
            LogUtils.d(CDNDownloadHandler.TAG, "!!!!!!!!!!!!!onFailure() method called and mCurStreamIndex is " + mCurStreamIndex + " and mStreamEntityListSize is " + mStreamEntityListSize);
            if (mCurStreamIndex < 0 || mCurStreamIndex >= mStreamEntityListSize - 1 || mStreamEntityListSize <= 0) {
                LogUtils.d(CDNDownloadHandler.TAG, "!!!!!!!!!!!!!!!retry all stream end and direct down failed!!!!!!!!!!!!!!!");
                downFail();
                if (mIsJSFailed) {
                    mPlayStatusReport.setEndRequestTime(System.currentTimeMillis());
                    reportDownloadState(MoviesConstant.JS_ERROR_SERVER_DOWNLOAD_FAIL);
                    mIsJSFailed = false;
                }
                if (mIsJSSuccessed) {
                    mPlayStatusReport.setEndRequestTime(System.currentTimeMillis());
                    reportDownloadState(MoviesConstant.JS_SUCCESS_URL_DOWNLOAD_ERROR);
                    mIsJSSuccessed = false;
                }
            } else {
                // 下载地址无效尝试下一个下载地址,切换其他清晰度
                LogUtils.d(CDNDownloadHandler.TAG, "!!!!!!!!!!!!!!!retry reason maybe is judge downfile outtime and excute down!!!!!!!!!!!!!!!");
                LogUtils.e(CDNDownloadHandler.TAG, "!!!!!!!!!!!!!!!retry reason maybe is resultUrl is unvalid!!!!!!!!!!!!!!!");
                changeStreamDown();
            }
        } else {
            downFail();
        }
    }

    public boolean pauseOnOther(int status) {
        LogUtils.d(CDNDownloadHandler.TAG, "-------pauseOnOther is called and current status is:" + status + "-------");
        mStatus = status;
        mDownloadManager.DOWNLOADING_NUM = 0;
        if (mListener != null)
            mListener.downloadPaused(this);

        if (downloadHandler != null) {
            downloadHandler.onPause(this);
        }
        return mDownloadTask.cancel(true);
    }

    /**
     * 当不开启后台下载退出时的处理
     */
    public void pauseOnExit() {
        mStatus = PAUSEONSPEED;
        mDownloadManager.DOWNLOADING_NUM = 0;
        if (mListener != null)
            mListener.downloadPaused(this);
        mDownloadTask.cancel(true);
    }

    private boolean isCanReTry() {
        LogUtils.i(Utils.DOWN_TAG, "mExceptionType:" + mExceptionType);
        if (mExceptionType != 0 && mExceptionType != NET_TIMEOUT)
            return false;
        switch (NetWorkTypeUtils.reportNetType(MoviesApplication.getInstance())) {
            case 1:
                return mRetryNum < 3 ? true : false;
            case 2:
                return isDownloadcan3g();
            default:
                return false;
        }
    }

    /**
     * 截流时，需要自动重试3次
     */
    private boolean isSnifferCanReTry() {
        LogUtils.i(Utils.DOWN_TAG, "isSnifferCanReTry--mExceptionType:" + mExceptionType);
        if (mEntity.getSite().equals("letv"))
            return false;
        if (mExceptionType != 0 && mExceptionType != NET_TIMEOUT)
            return false;
        switch (NetWorkTypeUtils.reportNetType(MoviesApplication.getInstance())) {
            case 1:
                return autoSnifferRetry;
            case 2:
                return isDownloadcan3g();
            default:
                return false;
        }
    }

    /**
     * 是否可以更换截流模式，mp4和m3u8之间的切换
     */
    private boolean isCanChangeSniffer() {
        LogUtils.i(Utils.DOWN_TAG, "isCanChangeSniffer");
        if (mEntity.getSite().equals("letv"))
            return false;
        if (mExceptionType != 0 && mExceptionType != NET_TIMEOUT)
            return false;
        switch (NetWorkTypeUtils.reportNetType(MoviesApplication.getInstance())) {
            case 1:
                return mTotalRetryNum == 6 ? true : false;
            case 2:
                return isDownloadcan3g();
            default:
                return false;
        }
    }

    public boolean isCurrentPathExist() {
        if (!StringUtil.isEmpty(mDestination)) {
            String tempPath = mDestination;
            if (tempPath.contains("/" + Utils.getDownLoadFolder())) {
                tempPath = tempPath.substring(0, tempPath.indexOf("/" + Utils.getDownLoadFolder()));
            } else if (tempPath.contains("/" + "kuaikan")) {
                tempPath = tempPath.substring(0, tempPath.indexOf("/" + "kuaikan"));
            }
            if (DownloadHelper.isSdcardExist(tempPath)) {
                return true;
            }
        }
        return false;
    }

    public void notifyDownloadAdded() {
        if (mListener != null)
            mListener.downloadStarted(this);
    }

    public void notifyDownloadStarted() {
        if (mListener != null)
            mListener.downloadOnDownloading(this);
    }

    public void notifyDownloadOnUpdate() {
        if (mListener != null)
            mListener.updateNotifyOnDownloading(this);
    }

    public void notifyDownloadOnPause() {
        if (mListener != null)
            mListener.downloadOnPause(this);
    }

    public void notifyDownloadEnded() {
        if (!mDownloadTask.isCancelled()) {
            LogUtils.i(Utils.DOWN_TAG, "下载成功：notifyDownloadEnded");
            if (mListener != null) {
                LogUtils.i(Utils.DOWN_TAG, "下载成功：notifyDownloadEnded---mListener");
                mListener.downloadEnded(this);
            } else {
                LogUtils.i(Utils.DOWN_TAG, "下载成功：notifyDownloadEnded---？？？？？");
                DownloadProvider mDownloadProvider = mDownloadManager.getProvider();
                mDownloadProvider.downloadCompleted(this);
            }
            mProgress = 100;
        }
    }

    public boolean updateDownloadEntity() {
        if (MoviesApplication.getInstance()
                .getDownloadManager().selectDownloadJobByMid(mEntity
                        .getId())) {
            return mDownloadManager.getProvider().updateDownloadEntity(this);
        }
        return false;
    }

    public boolean isCancelled() {
        return mDownloadTask.isCancelled();
    }

    public DownloadHandler getDownloadHandler() {
        return downloadHandler;
    }

    public void setDownloadHandler(DownloadHandler downloadHandler) {
        this.downloadHandler = downloadHandler;
    }

    /**
     * 相当于播放中的 doPlay
     *
     * @return
     * @throws Exception
     */
    public int downloadFile() throws Exception {
        return downloadHandler.downloadFile(this);
    }

    public String getmDestination() {
        return mDestination;
    }

    public void setmDestination(String mDestination) {
        this.mDestination = mDestination;
    }

    public boolean getCheck() {
        return isCheck;
    }

    public void setCheck(boolean isCheck) {
        this.isCheck = isCheck;
    }

    /**
     * 添加当前上报的状态
     *
     * @param state M300 M3u8 无法下载
     *              M301 M3u8 下载正常
     *              M400 Mp4 无法下载
     *              M401 Mp4 下载正常
     *              M310 M3u8 解析失败
     *              M311 M3u8 解析成功
     *              M410 Mp4 解析失败
     *              M411 Mp4 解析成功
     *              M312 M3U8下载过程中失败
     *              M412 Mp4下载过程中失败
     */
    public void addReportState(String state) {
        // 只有站外源才需要劫流上报对象
        String site = mEntity.getSite();
        if (PlayerUtils.isOutSite(site) || ("letv".equals(mEntity.getSite()) && !"1".equals(mEntity.getIsletvjiexi()))) {
            if (null == mStateList) {
                mStateList = new ArrayList<String>();
            }
            if (!mStateList.contains(state)) {
                mStateList.add(state);
            }
        }
    }

    /**
     * 为m3u8下载提供的一个计算速度的变量
     */
    public void setmM3u8DownloadedSize(long mM3u8DownloadedSize) {
        this.mM3u8DownloadedSize += mM3u8DownloadedSize;
    }

    // newStream code start

    /**
     * 新外网截流
     */
    private void requestOutSiteStreamInfo(List<String> apiContent) {
        mSnifferUrl = mEntity.getSnifferUrl();
        LogCat.e(Utils.DOWN_TAG, "out site mSnifferUrl is " + mSnifferUrl);
        LogCat.e(Utils.DOWN_TAG, "out site down order is " + mEntity.getIndex());
        mOutSiteStreamInfoTask = new RequestOutSiteStreamInfoTask(mContext);
        if (null != mOutSiteStreamInfoTask && !mOutSiteStreamInfoTask.isCancelled()) {
            mOutSiteStreamInfoTask.cancel();
        }

        mPlayStatusReport = new PlayStatusReporter();
        mPlayStatusReport.setAction(MoviesConstant.STATE_ACTION_DOWNLOAD);
        mPlayStatusReport.setPorder(mEntity.getPorder());
        mPlayStatusReport.setVid(mSnifferUrl);
        mPlayStatusReport.setSubsrc(mEntity.getSite());
        mPlayStatusReport.setSrartRequestTime(System.currentTimeMillis());
        mPlayStatusReport.setAid(mEntity.getMid());
        mPlayStatusReport.setExternal_id(mEntity.getExternal_id());
        mPlayStatusReport.setGlobal_vid(mEntity.getGlobaVid());
        mOutSiteStreamInfoTask.setType(Utils.STREAM_DOWN); // 控制下载优先级
        mOutSiteStreamInfoTask.setUrl(mSnifferUrl);
        if(TextUtils.isEmpty(outSiteFormat)){
            outSiteFormat = mEntity.getOutsiteClarity();
        }
        if(TextUtils.isEmpty(outSiteFormat)){
            outSiteFormat = MoviesConstant.SUPER;
        }
        if (apiContent != null) {
            mOutSiteStreamInfoTask.setApiContent(apiContent);
        } else {
            isSpecialSniff = false;
        }
        mOutSiteStreamInfoTask.setFormats(outSiteFormat);
        mOutSiteStreamInfoTask.setListener(new OutSiteStreamInfoRequestListener());
        mOutSiteStreamInfoTask.start();
    }

    public class OutSiteStreamInfoRequestListener implements
            NewRequestResultListener<OutSiteStreamInfo> {
        @Override
        public boolean onRequestFailed() {
            downFail();
            LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!OutSiteStreamInfo requestFailed and down failed!!!!!!!!!");
            mPlayStatusReport.setRetry("" + retryTime);
            mPlayStatusReport.setEndRequestTime(System.currentTimeMillis());
            reportPlayState(MoviesConstant.SERVER_DOWNLOAD_RESULT_ERROR);
            return false;
        }
        @Override
        public boolean onNoNet() {
            downFail();
            mPlayStatusReport.setRetry("" + retryTime);
            mPlayStatusReport.setEndRequestTime(System.currentTimeMillis());
            reportPlayState(MoviesConstant.SERVER_DOWNLOAD_NETS_ERROR);
            return false;
        }

        @Override
        public void onRequestSuccess(int updateId, OutSiteStreamInfo result) {
            LogUtils.e(Utils.DOWN_TAG, "onRequestSuccess method OutSiteStreamInfo is " + result);
            mPlayStatusReport.setEndRequestTime(System.currentTimeMillis());
            mPlayStatusReport.setRetry("" + retryTime);
            if (result == null) {
                isNeedRetry = true;
            } else {
                isNeedRetry = checkIsNeedRetry(result);
            }
            LogCat.e(Utils.DOWN_TAG, "onRequestSuccess method end and final isNeedRetry is " + isNeedRetry);
            LogCat.e(Utils.DOWN_TAG, "############current retryTime is " + retryTime);
            if (isNeedRetry) {
                reportPlayState(MoviesConstant.GET_DOWNLOADURL_FAIL_STATE);
                if (retryTime < 1) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            LogCat.e(Utils.DOWN_TAG, "first again try request OutSiteStreamInfo");
                            retryTime++;
                            if (mApi_contentlist != null && mApi_contentlist.size() > 0) {
                                requestOutSiteStreamInfo(mApi_contentlist);
                            } else {
                                requestOutSiteStreamInfo(null);
                            }
                        }
                    }, 1500);
                } else {
                    LogCat.e(Utils.DOWN_TAG, "second try request OutSiteStreamInfo not allowed");
                    //TODO 所有stream_list都为空（但此时api_list不为空也无需考虑，下载失败）
                    LogCat.e(Utils.DOWN_TAG, "maybe server error lead to no data return and down failed");
                    mPlayStatusReport.setEndRequestTime(System.currentTimeMillis());
                    reportPlayState(MoviesConstant.GET_DOWNLOADURL_FAIL_STATE);
                    if (mApi_contentlist != null) {
                        mApi_contentlist = null;
                        isSpecialSniff = false;
                        retryTime = 0;
                        changeStreamDown();
                    } else {
                        downFail();// data null err
                    }
                }
            } else {
                if(result != null && result.getAllFormatsList().contains(outSiteFormat)){
                    executeOutSiteDownLoad(result);
                }else{
                    if(result != null){
                        boolean isChangeformats = false;
                        while(!result.getAllFormatsList().contains(outSiteFormat)){
                            if (MoviesConstant.NORMAL.equalsIgnoreCase(outSiteFormat)){
                                break;
                            }else if(MoviesConstant.HIGH.equalsIgnoreCase(outSiteFormat)){
                                outSiteFormat = MoviesConstant.NORMAL;
                                isChangeformats = true;
                            }else if (MoviesConstant.SUPER.equalsIgnoreCase(outSiteFormat)){
                                outSiteFormat = MoviesConstant.HIGH;
                                isChangeformats = true;
                            }else if (MoviesConstant.SUPER2.equalsIgnoreCase(outSiteFormat)){
                                outSiteFormat = MoviesConstant.SUPER;
                                isChangeformats = true;
                            }else if (MoviesConstant.REAL.equalsIgnoreCase(outSiteFormat)){
                                outSiteFormat = MoviesConstant.SUPER2;
                                isChangeformats = true;
                            }
                        }
                        if(isChangeformats){
                            retryTime = 0;
                            requestOutSiteStreamInfo(null);
                        }
                    }
                }
            }
        }

        @Override
        public void onPreRequest() {
        }
    }

    /**
     * 不需要截流走的下载逻辑或者截流失败逻辑
     */
    public void handlerOutSiteDownError() {
        if (mStream_list != null && !mStream_list.isEmpty()) {
            LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!handlerOutSiteDownError method called!!!!!!!");
            LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!excuteOutSiteDownTask method called!!!!!!!");
            LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!cur down definition is " + mRequestFormat);
            LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!cur down allow definition is " + mAllowFormats);
            LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!mStream_list size is " + mStream_list.size() + " and mStream_list is " + mStream_list);
            // TODO 多个地址
            if (mStream_list.size() > 1 && 1 != new HashSet<String>(mStream_list).size()) {
                // 该下载地址忽略，直接使用下一个流地址
//                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!!not need sniff retry reason is downUrl has different piece!!!!!!!!!!!!!!!");
//                changeStreamDown();
//                return;
            } else
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!!not need sniff downUrl only has one piece!!!!!!!!!!!!!!!");
            mIsJSFailed = true;
            excuteOutSiteDownTask(mStream_list);
        } else {
            onFailure();
        }
    }

    /**
     * 新流外网下载
     *
     * @param streamInfo(此时传入的参数一定不为空)
     */
    private void executeOutSiteDownLoad(OutSiteStreamInfo streamInfo) {
        mOutSiteStreamInfo = streamInfo;
        LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!!!!!!!executeOutSiteDownLoad method streamInfo is!!!!!!!!!" + streamInfo);
        excuteCoreDownLogic(mCurStreamIndex);
//        excuteCoreDownLogic(1);
    }

    /**
     * 核心下载逻辑
     *
     * @param index
     */
    private void excuteCoreDownLogic(int index) {
        isHasRule = false;
        isNeedJsCut = false;
        isSniffDone = false;
        mIsJSFailed = false;
        mIsJSSuccessed = false;
        if (mOutSiteStreamInfo != null) {
            mStreamEntityList = mOutSiteStreamInfo.getStreamEntityQueue();
            if (null == mStreamEntityList) {
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!mStreamEntityList is null!!!!!!!!!!");
                downFail();
                return;
            }
            mStreamEntityListSize = mStreamEntityList.size();
            if (index < 0 || index >= mStreamEntityListSize || mStreamEntityListSize <= 0) {
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!mCurStreamIndex out index!!!!!!!!!!");
                downFail();
                return;
            }
            mOutSiteStreamEntity = mStreamEntityList.get(index);
            LogCat.e(Utils.DOWN_TAG, "curIndex is " + index + " and mOutSiteStreamEntity is " + mOutSiteStreamEntity);
            mAllowFormats = mOutSiteStreamEntity.getAllowFormats();
            mRequestFormat = mOutSiteStreamEntity.getRequestFormat();
            mOsType = mOutSiteStreamEntity.getOsType();
            mEid = mOutSiteStreamEntity.getEid();
            mStream_list = mOutSiteStreamEntity.getStream_list();
            mApi_list = mOutSiteStreamEntity.getApi_list();
            mUser_agent = mOutSiteStreamEntity.getUser_agent();
            fSizeList = mOutSiteStreamEntity.getfSizeList();
            totalSize = mOutSiteStreamEntity.getTotalSize();
            getIsHasRule();
            printAllValue(index);

            if ("youku".equals(mEntity.getSite()) && "THREE_PARSES".equals(mOutSiteStreamEntity.getClientParseType()) &&
                    mApi_list != null && !mApi_list.isEmpty() && !isSpecialSniff) {
                isSpecialSniff = true;
                executeSpecialLogic();
                return;
            }

            // 预判断是否需要js截流( mApi_list||rule 为空-->false     mApi_list&&rule 非空-->true)
            if (null != mApi_list && mStream_list != null && !mApi_list.isEmpty() && isHasRule
                    && mApi_list.size() == mStream_list.size() && !isSpecialSniff) {
                LogCat.e(Utils.DOWN_TAG, "prejudge need jsSniff");
                isNeedJsCut = true;
            } else {
                LogCat.e(Utils.DOWN_TAG, "prejudge not need jsSniff");
                isNeedJsCut = false;
            }
            // 截流逻辑
            if (isNeedJsCut) {
                // 需要截流
                if (mStream_list != null && !mStream_list.isEmpty()) {
                    // TODO 截流入口  确保每个api都执行请求apiContent
                    mApi_contentlist = new ArrayList();
                    mResultUrlList = new ArrayList<>();
                    downloadRequestManager = new DownloadRequestManager(mContext, this);
                    LogCat.e(Utils.DOWN_TAG, "prepare start sniff");
                    excuteSniffLogic();
                } else {
                    // 切换其他清晰度
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!want jsSniff but lack necessary mStream_list param!!!!!!!");
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!onlineDown outsite changeStream!!!!!!!");
                    changeStreamDown();
                }
            } else {
                // 不需要截流（直接取stream_list来下载）
                LogCat.e(Utils.DOWN_TAG, "not need jsSniff and direct down if can or changeStream");
                if (mStream_list != null && !mStream_list.isEmpty()) {
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!excuteOutSiteDownTask method called!!!!!!!");
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!cur down definition is " + mRequestFormat);
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!cur down allow definition is " + mAllowFormats);
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!mStream_list size is " + mStream_list.size() + " and mStream_list is " + mStream_list);
                    excuteOutSiteDownTask(mStream_list);
                } else {
                    // 切换其他清晰度
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!want direct play but lack necessary mStream_list url!!!!!!!");
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!onlineDown outsite changeStream!!!!!!!");
                    changeStreamDown();
                }
            }
        } else
            downFail();
    }

    /**
     * 打印当前流实体的参数
     *
     * @param index
     */
    private void printAllValue(int index) {
        LogCat.e(Utils.DOWN_TAG, "curIndex is " + index + " and all params vaule is {" +
                "allowFormats=" + mAllowFormats +
                ", requestFormat='" + mRequestFormat + '\'' +
                ", osType='" + mOsType + '\'' +
                ", eid='" + mEid + '\'' +
                ", stream_list=" + mStream_list +
                ", api_list=" + mApi_list +
                ", user_agent='" + mUser_agent + '\'' +
                ", isHasRule=" + isHasRule +
                ", te='" + mTe + '\'' +
                ", ts='" + mTs + '\'' +
                '}');
    }

    /**
     * 判断流实体是否有js截流参数
     */
    private void getIsHasRule() {
        if (mOutSiteStreamEntity.isHasRule()) {
            mJsRuleMap = mOutSiteStreamEntity.getRule();
            if (null == mJsRuleMap)
                isHasRule = false;
            else {
                if (mJsRuleMap.isEmpty())
                    isHasRule = false;
                else {
                    mTe = mJsRuleMap.get(TE);
                    mTs = mJsRuleMap.get(TS);
                    if (!TextUtils.isEmpty(mTe) && !TextUtils.isEmpty(mTs))
                        isHasRule = true;
                    else
                        isHasRule = false;
                }
            }
        } else
            isHasRule = false;
    }

    /**
     * 遍历streamEntityQueue列表中每个OutSiteStreamEntity，当且仅当OutSiteStreamEntity的stream_list都为空，则返回true
     *
     * @param streamInfo
     * @return
     */
    private boolean checkIsNeedRetry(OutSiteStreamInfo streamInfo) {
        if (streamInfo.isRetry()) {
            LogCat.e(Utils.DOWN_TAG, "checkIsNeedRetry method streamInfo newversion is null");
            return true;
        }
        boolean isNeedRetry = false;
        List<OutSiteStreamEntity> list = streamInfo.getStreamEntityQueue();
        if (list != null && list.size() > 0) {
            int size = list.size();
            int count = 0; //list中 stream_list 为空的数量
            for (OutSiteStreamEntity entity : list) {
                if (null != entity && "THREE_PARSES".equals(entity.getClientParseType()) &&
                        null != entity.getApi_list() && entity.getApi_list().size() > 0) {
                    // 无需操作
                } else if (null != entity && null != entity.getStream_list() && entity.getStream_list().size() > 0) {
                    mApi_contentlist = null;
                    // 无需操作
                } else
                    count++;
            }
            if (count == size) {
                isNeedRetry = true;
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!! all streamEntity stream_list both is null !!!!!!!!!!!!");
            } else {
                isNeedRetry = false;
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!! have stream_list !!!!!!!!!!!!");
            }
        } else
            isNeedRetry = true;
        LogCat.e(Utils.DOWN_TAG, "checkIsNeedRetry method end and isNeedRetry is " + isNeedRetry);
        return isNeedRetry;
    }

    /**
     * 类似于播放中的doPlay方法
     */
    private void excuteCommonDownTask() {
        LogUtils.e(TAG, "!!!!!!!!!!!!!！excuteCommonDownTask() method called !!!!!!!!!!!!!!");
        if(AsyncTask.Status.RUNNING != mDownloadTask.getStatus()){
            mDownloadTask.setmPlayStatusReport(mPlayStatusReport);
            if (Utils.getAPILevel() >= 11) {
                mDownloadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            } else {
                mDownloadTask.execute();
            }
            mDownloadManager.notifyObservers();
        }
    }

    /**
     * 外站源下载
     *
     * @param resultUrl
     */
    private void excuteOutSiteDownTask(List<String> resultUrl) {
        LogUtils.e(TAG, "!!!!!!!!!!!!!！excuteOutSiteDownTask() method called !!!!!!!!!!!!!!");
        mDownloadTask = new DownloadTask(this);
        mDownloadTask.setmResultUrlList(resultUrl);
        excuteCommonDownTask();
    }

    /**
     * 重置外站源下载数据
     */
    public void resetOutSiteData() {
        if (PlayerUtils.isOutSite(mEntity.getSite()) || ("letv".equals(mEntity.getSite()))) {
            LogUtils.d(CDNDownloadHandler.TAG, "!!!!!!!!!!resetOutSiteData called!!!!!!!!!!");
            retryTime = 0;
            isNeedRetry = false;
            mCurStreamIndex = 0;
        }
    }

    /**
     * 截流逻辑
     */
    private void excuteSniffLogic() {
        requestSnifferAllApiData();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                circleExcuteSniff();
            }
        }, PlayerUtils.SNIFF_REQUESTAPI_WAITETIME);
    }

    /**
     * 优酷特殊逻辑
     */

    private void executeSpecialLogic() {
        requestSnifferAllApiData();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                RequestSniffDataTaskPoolManager.get().shutdown();
                while (!RequestSniffDataTaskPoolManager.get().isTerminated()) {
                    //等待执行完毕
                    LogCat.e(Utils.TAG, " wait for pool task finish ");
                }
                mApi_contentlist = RequestSniffDataTaskPoolManager.get().getResultList();

                RequestSniffDataTaskPoolManager.get().shutdown();
                if (mApi_contentlist == null || mApi_contentlist.isEmpty()) {
                    LogCat.e(Utils.TAG, " request apiContentList is 0 ！ ");
                    handlerOutSiteDownError();
                    return;
                }

                if (mApi_contentlist.size() > 0) {
                    resetOutSiteData();
                    retryTime = 0;
                    requestOutSiteStreamInfo(mApi_contentlist);
                }

            }
        }, PlayerUtils.SNIFF_REQUESTAPI_WAITETIME);
    }


    /**
     * 一次性取到所有截流所需的 apiContent 内容放到 mApi_contentlist中
     */
    private void requestSnifferAllApiData() {
        if (mApi_list != null && !mApi_list.isEmpty()) {
            LogCat.e(Utils.DOWN_TAG, "js apiList size is " + mApi_list.size());
            // 多线请求api
            try {
                RequestSniffDataTaskPoolManager.get().createPool();
                RequestSniffDataTaskPoolManager.get().exeTaskTimeOut(mApi_list, mSnifferUrl, mUser_agent, 2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 循环执行本地js截流逻辑
     */
    private void circleExcuteSniff() {
        // 关闭线程池不接受新的任务，如果此时后台有未执行完毕的任务取消掉直接，
        RequestSniffDataTaskPoolManager.get().shutdown();
        while (!RequestSniffDataTaskPoolManager.get().isTerminated()) {
            //等待执行完毕
            LogCat.e(Utils.DOWN_TAG, " wait for pool task finish ");
        }
        mApi_contentlist = RequestSniffDataTaskPoolManager.get().getResultList();

        RequestSniffDataTaskPoolManager.get().shutdown();
        if (mApi_contentlist == null || mApi_contentlist.isEmpty()) {
            LogCat.e(Utils.DOWN_TAG, " request apiContentList is 0 ！ ");
            handlerOutSiteDownError();
            return;
        }
        if (mStream_list == null || mStream_list.isEmpty()) {
            LogCat.e(Utils.DOWN_TAG, " request streamList is 0 ！ ");
            handlerOutSiteDownError();
            return;
        }
        LogCat.e(Utils.DOWN_TAG, " out of time set apicontentList " + mApi_contentlist.size());
        if (mApi_contentlist != null && !mApi_contentlist.isEmpty() && mStream_list != null && !mStream_list.isEmpty()) {
            LogCat.e(Utils.DOWN_TAG, " send js apicontentList size " + mApi_contentlist.size());
            LogCat.e(Utils.DOWN_TAG, " send js StreamList size " + mStream_list.size());
            //TODO 逻辑简单处理，待优化
            if ((mApi_contentlist.size() > 1 && 1 != new HashSet<String>(mApi_contentlist).size()) || (mStream_list.size() > 1 && 1 != new HashSet<String>(mStream_list).size())) {
                // 该下载地址忽略，直接使用下一个流地址
//                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!!need sniff retry reason is downUrl has different piece!!!!!!!!!!!!!!!");
//                changeStreamDown();
//                return;
            } else
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!!need sniff downUrl only has one piece!!!!!!!!!!!!!!!");
            mApiContentListIndex = 0;
            mStreamListIndex = 0;
            doSniff(); // 第一次截流启动
        } else {
            LogCat.e(Utils.DOWN_TAG, "sniff params error");
            handlerOutSiteDownError();
        }
    }

    /**
     * @param requestUrl 原始请求的url
     * @param apiContent HttpGet请求api_list 中的每一条数据对应的地址，得到的内容即为apiContent
     * @param streamUrl  stream_list中的每一条数据
     */
    private void excuteCoreSniff(String requestUrl, List<String> apiContent, List<String> streamUrl) {
        JSONObject obj = new JSONObject();
        LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!excuteCoreSniff!!!!!!!!!! and param : requestUrl is " + requestUrl + " and streamUrl " + streamUrl);
        try {
            obj.put("requestUrl", Base64.encodeToString(requestUrl.getBytes(), Base64.DEFAULT));
            JSONArray array = new JSONArray();
            for (String item : streamUrl) {
                String str = Base64.encodeToString(item.getBytes(), Base64.DEFAULT);
                array.put(str.replace("\\n", ""));
            }
            obj.put("uStream", array);
            array = new JSONArray();
            for (String url : apiContent) {
                String str = Base64.encodeToString(url.getBytes(), Base64.DEFAULT);
                array.put(str.replace("\\n", ""));
            }
            obj.put("apiContent", array);
            // isHasRule一定为true
            if (isHasRule) {
                LogCat.e(Utils.DOWN_TAG, " js has rule");
                JSONObject ruleObj = new JSONObject();
                if (!TextUtils.isEmpty(mTs)) {
                    ruleObj.put(TS, mTs);
                }
                if (!TextUtils.isEmpty(mTe)) {
                    ruleObj.put(TE, mTe);
                }
                obj.put("rule", ruleObj);
                LogCat.e(Utils.DOWN_TAG, " js rule " + ruleObj.toString());
                LogCat.e(Utils.DOWN_TAG, " js ts te (" + mTs + ")  (" + mTe + ")");
            } else {
                LogCat.e(Utils.DOWN_TAG, " js no rule");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String snifferParamter = obj.toString().replace("\\n", "");
        String fileName = "jsSniffRequestParams.html";
        Utils.writeJsSniffLogData(mContext, fileName, snifferParamter);
        LogCat.e(Utils.DOWN_TAG, "@@@@@@@" + snifferParamter);
        if (null == downloadRequestManager)
            downloadRequestManager = new DownloadRequestManager(mContext, this);
        // 调用js代码
        downloadRequestManager.excuteJsSniffLogic(snifferParamter);
    }

    /**
     * js 解析结果处理，有地址就播放
     *
     * @param result
     */
    public void parseSniffResult(String result) {
        LogCat.e(Utils.DOWN_TAG, "Js parse return back !" + result);
        try {
            JSONObject obj = new JSONObject(result);
            // TODO stream串可能有多个地址
            String stream = obj.getString("stream");
            JSONArray array = new JSONObject(result).optJSONArray("stream");
            if (array == null || array.length() == 0) {
                // 本次截流失败
                mDownJsSniffResultListener.sniffFail();
                LogCat.e(Utils.DOWN_TAG, " sniff finish result error");
                handlerOutSiteDownError();// 逻辑完善后去掉注释
                // streamList有误！！！！该下载地址忽略，直接使用下一个流地址
//                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!!sniff failed not direct down but changeStream!!!!!!!!!!!!!!!");
//                changeStreamDown();
                return;
            } else {
                // 本次截流成功
                for (int i = 0; i < array.length(); i++) {
                    mResultUrlList.add(array.getString(i));
                }
                mDownJsSniffResultListener.sniffSucess();
                LogCat.e(Utils.DOWN_TAG, " sniff finish result ok");
                String fileName = "jsSniffResult.html";
                Utils.writeJsSniffLogData(mContext, fileName, stream);
                if (isSniffDone) {
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!!!!all js sniff sucess!!!!!!!!!!!!!!!!!!");
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!excuteOutSiteDownTask method called!!!!!!!");
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!cur down definition is " + mRequestFormat);
                    LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!cur down allow definition is " + mAllowFormats);
                    mIsJSSuccessed = true;
                    excuteOutSiteDownTask(mResultUrlList);
                    // 截流成功上报
                    excuteUpLoadLocalStreamTask(mResultUrlList);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            LogCat.e(Utils.DOWN_TAG, "Js parse result err and msg is " + e.getMessage());
            handlerOutSiteDownError();
        }
    }

    /**
     * 切换其他清晰度下载
     */
    public void changeStreamDown() {
        mDownloadManager.DOWNLOADING_NUM = 0;
        LogUtils.d(CDNDownloadHandler.TAG, "!!!!!!!onlineDown outsite changeStream!!!!!!!");
        mCurStreamIndex++;
        start();
    }

    /**
     * 相当于播放跳外网(下载终点)
     */
    private void downFail() {
        boolean isLetvNeedJiexi = "letv".equals(mEntity.getSite()) && !"1".equals(mEntity.getIsletvjiexi()) && mProgress<=0 && "1".equals(SharePreferenceManager.getVaule(MoviesApplication.getInstance(), SharePreferenceManager.LETVDOWNLOAD, "0"));
        if(isLetvNeedJiexi){
            LogUtils.d(DownloadDaoImpl.TAG, "------downFail and need jiexi so updateDownloadEntity-----");
            mDownloadManager.DOWNLOADING_NUM = 0;
            mEntity.setIsletvjiexi("1");
            updateDownloadEntity();
            start();        //乐视源截流失败后走解析
            return;
        }
        LogUtils.d(CDNDownloadHandler.TAG, "!!!!!!!!!!!!!!!!downFail() called and if is outsite need resetOutSiteData!!!!!!!!!!!!!!!");
        resetOutSiteData();
        mDownloadManager.DOWNLOADING_NUM = 0;
        LogUtils.d(CDNDownloadHandler.TAG, "onFailure use time is " + (System.currentTimeMillis() - startTime) + "ms");
        LogUtils.d(CDNDownloadHandler.TAG, "$$$$$$$$$$$$缓冲失败$$$$$$$$$$$$");
        mStatus = DownloadJob.NO_USER_PAUSE;
        LogUtils.d(CDNDownloadHandler.TAG, "------set state is DownloadJob.NO_USER_PAUSE------");
//        if (MoviesApplication.getInstance().getRunningPackageName().equals(MoviesApplication
//                .YINGSHIDAQUAN_PACKAGENAME)) {
//            Toast.makeText(mContext, mContext.getResources().getString(R.string.pause_download_no_network), Toast.LENGTH_SHORT).show();
//        } else {
//            DownloadBroadcastReceiver.setNotification(mContext, mContext.getResources().getString(R.string.download_pause_title),
//                    mContext.getResources().getString(R.string.pause_download_no_network));
//        }
        if (downloadHandler != null) {
            downloadHandler.onPause(this);
        }
        notifyDownloadOnPause();
        // mJob.setmExceptionType(mExceptionType);
        if (!NetWorkTypeUtils.isNetworkAvailable(MoviesApplication.getInstance())) {
            mExceptionType = NET_SHUT_DOWN; // 无网络
            mLastConnectivityState = NetWorkTypeUtils.NETTYPE_NO;
            LogUtils.d(CDNDownloadHandler.TAG, "No net work");
        } else if (NetWorkTypeUtils.reportNetType(MoviesApplication.getInstance()) != 1 && (!isDownloadcan3g())) {
            //3g情况下，不允许下载，不做任何处理，和暂停相同
        } else {
//				mStatus = DownloadJob.DOWNLOAD_FALIE_STATE;//下载失败
            LogUtils.d(CDNDownloadHandler.TAG, "Download error, and pause");
            mExceptionType = DOWNLOAD_FAILUER;//下载失败
            pauseByDownLoadFailure();//下载失败，相当于用户暂停
            //上报
            if (!autoSnifferRetry) {
                addReportState(PlayerUtils.M410);
            }
            if (mProgress > 0) {
                addReportState(PlayerUtils.M412);
            }
        }
        mDownloadManager.notifyObservers();
    }

    /**
     * 下载js截流回调（播放没有同步此逻辑代码）
     */
    private PlayOrDownJsSniffListener mDownJsSniffResultListener = new PlayOrDownJsSniffListener() {
        @Override
        public void sniffSucess() {
            int apiSize = mApi_contentlist.size();
            int streamSize = mStream_list.size();
            if (mApiContentListIndex >= 0 && mApiContentListIndex < apiSize) {
                isSniffDone = true;
                LogUtils.e(Utils.DOWN_TAG, "################all url traverse end################");
                LogCat.e(Utils.DOWN_TAG, "###############mResultUrlList is " + mResultUrlList.size());
                return;
//                // 全部轮询完毕
//                if (mApiContentListIndex == apiSize - 1 && mStreamListIndex == streamSize - 1) {
//                    isSniffDone = true;
//                    LogUtils.e(Utils.DOWN_TAG, "################all url traverse end################");
//                    LogCat.e(Utils.DOWN_TAG, "###############mResultUrlList is " + mResultUrlList.size());
//                    return;
//                }
//                if (mStreamListIndex >= 0 && mStreamListIndex < streamSize) {
//                    if (mStreamListIndex == streamSize - 1) {
//                        LogUtils.e(Utils.DOWN_TAG, "################need add apiIndex and reset streamIndex 0################");
//                        // 一轮mStream_list已经遍历完成，开始新的一轮
//                        mStreamListIndex = 0;
//                        mApiContentListIndex++;
//                        doSniff();
//                    } else {
//                        // 正常遍历streamList
//                        mStreamListIndex++;
//                        doSniff();
//                    }
//                }
            }
        }

        /**
         * 截流一旦失败，就不会继续往下截流
         */
        @Override
        public void sniffFail() {
            LogCat.e(Utils.DOWN_TAG, "################sniffFail() called################");
            isSniffDone = false;
            mApiContentListIndex = 0;
            mStreamListIndex = 0;
        }
    };

    /**
     * 开始截流
     */
    private void doSniff() {
        if (mApiContentListIndex < 0 || mApiContentListIndex >= mApi_contentlist.size() || mStreamListIndex < 0 || mStreamListIndex >= mStream_list.size()) {
            LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!!!doSniff() called and out of index!!!!!!!!!!!");
            return;
        }
        LogCat.e(Utils.DOWN_TAG, "##################doSniff() called##################");
        LogCat.e(Utils.DOWN_TAG, " send js data (api-content) " + mApi_contentlist.get(mApiContentListIndex));
        LogCat.e(Utils.DOWN_TAG, " send js data (stream-list) " + mStream_list.get(mStreamListIndex));
        excuteCoreSniff(mSnifferUrl, mApi_contentlist, mStream_list);
    }

    /**
     * 截流成功上报
     */
    private void excuteUpLoadLocalStreamTask(List<String> stream_list) {
        if (null != mUpLoadLocalStreamTask && !mUpLoadLocalStreamTask.isCancelled())
            mUpLoadLocalStreamTask.cancel();
        mUpLoadLocalStreamTask = new UpLoadLocalStreamTask(mContext, mAllowFormats, mRequestFormat, mOsType, mSnifferUrl);
        mUpLoadLocalStreamTask.setStream_list(stream_list);
        mUpLoadLocalStreamTask.setListener(new UpLoadLocalStreamListener());
        mUpLoadLocalStreamTask.start();
    }

    private class UpLoadLocalStreamListener implements RequestResultListener<UpLoadLocalStreamBean> {
        @Override
        public boolean onRequestFailed() {
            LogUtils.e(Utils.DOWN_TAG, "!!!!!!!!!!!UpLoadLocalStream fail!!!!!!!!!!!!!!!!!!");
            return false;
        }

        @Override
        public void onRequestSuccess(int updateId, UpLoadLocalStreamBean result) {
            if (null != result)
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!UpLoadLocalStream sucess!!!!!!!!!!!!!!!!!!");
            else
                LogCat.e(Utils.DOWN_TAG, "!!!!!!!!!!!UpLoadLocalStream fail!!!!!!!!!!!!!!!!!!");
        }

        @Override
        public void onPreRequest() {

        }
    }

    public void setIsPauseByNetWork(boolean isPauseByNetWork) {
        mIsPauseByNetWork = isPauseByNetWork;
    }

    public boolean getIsPauseByNetWork() {
        return mIsPauseByNetWork;
    }

    public void setLastConnectivityState(int lastState) {
        mLastConnectivityState = lastState;
    }

    public int getLastConnectivityState() {
        return mLastConnectivityState;
    }

    public void setIsAllow3GDownload(boolean isAllow3GDownload) {
        mIsAllow3GDownload = isAllow3GDownload;
    }

    public boolean getIsAllow3GDownload() {
        return mIsAllow3GDownload;
    }

    public void setmPlayStatusReport(PlayStatusReporter mPlayStatusReport) {
        this.mPlayStatusReport = mPlayStatusReport;
    }

    private void reportPlayState(String state){
        mPlayStatusReport.setStartPlayTime(System.currentTimeMillis());
        mPlayStatusReport.setState(state);
        PlayStatusReporter playStatus = null;
        try {
            playStatus = mPlayStatusReport.clone();
        } catch (CloneNotSupportedException e) {

        }
        new ReportPlayStatusTask(MoviesApplication.getInstance(), playStatus).start();
    }

    public void reportDownloadState(String state){
        mPlayStatusReport.setStartPlayTime(System.currentTimeMillis());
        mPlayStatusReport.setState(state);
        PlayStatusReporter playStatus = null;
        try {
            playStatus = mPlayStatusReport.clone();
        } catch (CloneNotSupportedException e) {

        }
        new ReportPlayStatusTask(MoviesApplication.getInstance(), playStatus).start();
    }

    public void setmCvcTask(CvcTask mCvcTask) {
        this.mCvcTask = mCvcTask;
    }

    public CvcTask getmCvcTask() {
        return mCvcTask;
    }
}
